<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title></title>

<script type="text/javascript" src="http://list-gadget.googlecode.com/svn/trunk/json2.js" ></script>
<script type="text/javascript" src="stub.js" ></script>

</head>

<body>

<div id="gadget" style="width:600px;height:400px;background-color:#cccccc;">
<style>
th{background-color: #E2E2E2;vertical-align: top;}
table{border-spacing: 2px 2px;border-color: gray;}
td{height:28px;vertical-align:top;}
tr.even{background-color: #F2F2F2;}
input{margin:0;}textarea{margin:0;}
span.tab{cursor:pointer; font-size:small; background-color:#ddd; border:solid 2px white}
span.selected{border-top:solid 1px black;border-right:solid 1px #ccc;border-left:solid 1px #666;}
.viewing .onlyEditing {display:none;}
</style>
<script type="text/javascript">
/*** Emile framework included ***/
// emile.js (c) 2009 Thomas Fuchs
// Licensed under the terms of the MIT license.

(function(emile, container){
  var parseEl = document.createElement('div'),
    props = ('backgroundColor borderBottomColor borderBottomWidth borderLeftColor borderLeftWidth '+
    'borderRightColor borderRightWidth borderSpacing borderTopColor borderTopWidth bottom color fontSize '+
    'fontWeight height left letterSpacing lineHeight marginBottom marginLeft marginRight marginTop maxHeight '+
    'maxWidth minHeight minWidth opacity outlineColor outlineOffset outlineWidth paddingBottom paddingLeft '+
    'paddingRight paddingTop right textIndent top width wordSpacing zIndex').split(' ');

  function parse(value){
    var v = parseFloat(value), u = value.replace(/^[\d\.]+/,'');
    return { value: isNaN(v) ? u : v, unit: isNaN(v) ? 'color' : u };
  }

  function normalize(style){
    var css, rules = {}, i = props.length, v;
    parseEl.innerHTML = '<div style="'+style+'"></div>';
    css = parseEl.childNodes[0].style;
    while(i--) if(v = css[props[i]]) rules[props[i]] = parse(v);
    return rules;
  }

  function color(source,target,pos){
    var i = 2, j, c, tmp, v = [], r = [];
    while(j=3,i--)
      if(arguments[i][0]=='r'){
        c = arguments[i].match(/\d+/g); while(j--) v.push(parseInt(c[j]));
      } else while(j--) v.push(parseInt(c.substr(1+j*2,2), 16));
    while(j--) { tmp = ~~(v[j+3]+(v[j]-v[j+3])*pos); r.push(tmp<0?0:tmp>255?255:tmp); }
    return 'rgb('+r.join(',')+')';
  }

  container[emile] = function(el, style, opts){
    el = typeof el == 'string' ? document.getElementById(el) : el;
    opts = opts || {};
    var target = normalize(style), comp = el.currentStyle ? el.currentStyle : getComputedStyle(el, null),
      prop, current = {}, start = +new Date, dur = opts.duration||200, finish = start+dur, interval,
      easing = opts.easing || function(pos){ return (-Math.cos(pos*Math.PI)/2) + 0.5; };
    for(prop in target) current[prop] = parse(comp[prop]);
    interval = setInterval(function(){
      var time = +new Date, pos = time>finish ? 1 : (time-start)/dur;
      for(prop in target)
        el.style[prop] = target[prop].unit == 'color' ?
          color(current[prop].value,target[prop].value,easing(pos)) :
          (current[prop].value+(target[prop].value-current[prop].value)*easing(pos)).toFixed(3) + target[prop].unit;
      if(time>finish) { clearInterval(interval); opts.after && opts.after(); }
    },10);
  }
})('emile', this);


/*** End of Emile ***/

var list = {
  stateUpdated:function ()
  {
    if(list.currentlyEditing)
    {
      return;
    }
    var style = "";
    var table = "";
    var settings = list.getSettings();
    style = list.createStyleblock(settings);
    var keys = wave.getState().getKeys();
    var items = [];
    for(var i = 0; i< keys.length; i++)
    {
      if(keys[i].indexOf("item_") == 0)
      {
        items.push(gadgets.json.parse(wave.getState().get(keys[i])));
      }
    }
    var sortField = settings.sortBy;
    var sortColumn = list.getColumnByName(settings, sortField);
    if(sortColumn == null)sortColumn = settings.columns[0];
    sortField = sortColumn.name;
    var alphabetical = function(a,b)
    {
      a = a[sortField]; b = b[sortField];
      if(typeof(a) != "string") return -1;
      if(typeof(b) != "string") return 1;
      return ((a.toLowerCase() < b.toLowerCase()) ? -1 : ((a.toLowerCase() >b.toLowerCase()) ? 1 : 0));
    };
    var sortorder = alphabetical;
    if(typeof(sortColumn.sort) == "function")sortorder = sortColumn.sort;
    items = items.sort(sortorder);

    table = list.createTableHtml(items);

    _gel("listDisplay").innerHTML = style + table;
    setTimeout(function(){gadgets.window.adjustHeight()}, 100);

    if(list.justUpdated)
    {
      var currColor = (_gel(list.justUpdated).className == "even"? '#f2f2f2' : '#ffffff');
      _gel(list.justUpdated).style.backgroundColor = currColor;
      emile(list.justUpdated, 'background-color:#ffff00;',
        {
          duration: 500,
          after: function()
            {
              emile(list.justUpdated, 'background-color:' + currColor, {duration:1000});
              list.justUpdated = undefined;
            }
        });
    }
    if(list.justAdded)
    {
      var id = list.justAdded;
      list.justAdded = undefined;
      list.editItem(id);
    }

  },
  modeUpdated: function(mode)
  {
    if(_gel("pane_settings").style.display == "")
    {
      list.closeSettingsEditor();
    }
    if(mode == wave.Mode.EDIT)
    {
      _gel("gadget-wrapper").className = "";
    }else{
      _gel("gadget-wrapper").className = "viewing";
    }
    list.stateUpdated();
  },
  createTableHtml: function (items)
  {
    var s = "<table>";
    var settings = list.getSettings();
    if(settings.showHeading)
    {
      s += "<tr class='head'>";
      for(var i = 0; i< settings.columns.length; i++)
      {
        s += "<th>" + (settings.columns[i].displayName || settings.columns[i].name) + "</th>";
      }
      if(wave.getMode() == wave.Mode.EDIT)
      {
        s += "<th></th>";
      }
      s += "</tr>";
    }
    for(var i = 0; i< items.length; i++)
    {
      s += "<tr class='" + (i%2==1 ? "even" : "odd") + "' id='" + items[i].id + "'>";
      for(var j = 0; j< settings.columns.length; j++)
      {
        s += "<td >" + (list.makehtml(items[i][settings.columns[j].name])) + "</td>";
      }
      if(wave.getMode() == wave.Mode.EDIT)
      {
        s += "<td class='editbtn'><a style=\"font-size:x-small;\" href=\"javascript:list.delItem('" + items[i].id + "')\">delete</a><br/><a style=\"font-size:x-small;\" href=\"javascript:list.editItem('" + items[i].id + "')\">edit</a></td>";
      }
      s += "</tr>";
    }
    s += "</table>";
    return s;
  },
  createStyleblock: function(settings)
  {
    if(typeof settings.style == "string")
    {
      return "<style>"+ settings.style +"</style>";
    }
    return "";
  },
  setSettings: function (settings)
  {
    wave.getState().submitDelta({settings: gadgets.json.stringify(settings)})
  },
  getSettings: function ()
  {
    if(wave.getState().get("settings") == null)
    {
      return {
        sortBy: "title",
        showHeading: true,
        columns : [
          /*{name: "id"},*/
          {name: "title"},
          {name: "descr", displayName: "info", multiline:true}
          ]
        }
    }else{
      return gadgets.json.parse(wave.getState().get("settings"));
    }
  },
  getColumnByName: function (settings, name)
  {
    for(var i = 0;i<settings.columns.length; i++)
    {
      if(settings.columns[i].name == name)return settings.columns[i];
    }
    return null;
  },
  justAdded: undefined,
  justUpdated: undefined,
  newItem: function ()
  {
    if(list.currentlyEditing != undefined)
    {
      //no adding while editing
      return;
    }
    var newID = "item_" + new Date().getTime();
    var title = "New item";
    var delta = {}; delta[newID] = gadgets.json.stringify({id: newID, title: title, descr: ''});
    list.justAdded = newID;
    wave.getState().submitDelta(delta);
  },
  delItem:function (id)
  {
    var delta = {}; delta[id] = null;
    wave.getState().submitDelta(delta);
  },
  editBoxes: [],
  currentlyEditing: undefined,
  editItem: function (id)
  {
    var tr = _gel(id);
    var columns = list.getSettings().columns;
    var hasHeadingRow = list.getSettings().showHeading;
    var state = gadgets.json.parse( wave.getState().get(id));

    list.currentlyEditing = id;
    var rows = _gel("listDisplay").getElementsByTagName("tr");
    for(var i = hasHeadingRow?1:0 ; i < rows.length; i++)
    {
      var row = rows[i];
      row.childNodes[row.childNodes.length -1].innerHTML = "";
    }
    tr.childNodes[row.childNodes.length -1].innerHTML = "<a style=\"font-size:x-small;\" href=\"javascript:list.cancelEdit()\">cancel</a><br/><a style=\"font-size:x-small;\" href=\"javascript:list.saveItem()\">save</a>";

    for(var i = 0; i < columns.length;i++)
    {
      var cell = tr.childNodes[i];
      var column = columns[i];
      var editor = document.createElement("input");
      if(column.multiline == true)
      {
        editor = document.createElement("textarea");
      }
      if(cell.clientWidth < 40)cell.style.width = 40;
      editor.style.width = cell.clientWidth;
      editor.style.height = cell.clientHeight;
      editor.style.position = "absolute";
      var pos = list.getElementPosition(cell);
      editor.style.top = pos.y;
      editor.style.left = pos.x;
      editor.value = state[column.name] || "";
      cell.appendChild(editor);
      list.editBoxes.push(editor);
    }
    list.editBoxes[0].select();
  },
  saveItem:function ()
  {
    var item = gadgets.json.parse(wave.getState().get(list.currentlyEditing));
    var columns = list.getSettings().columns;
    var validationErrors = 0;
    for(var i = 0; i<list.editBoxes.length; i++)
    {
        if(columns[i].required)
        {
          var trimmed = list.editBoxes[i].value.replace(/^\s*/, "").replace(/\s*$/, "");
          if(trimmed == "")
          {
            list.editBoxes[i].style.backgroundColor = "#ffaaaa";
            validationErrors++;
          }
        }
        // validation?
        item[columns[i].name] = list.editBoxes[i].value;
    }
    if(validationErrors > 0)return;
    var delta = {}; delta[list.currentlyEditing] = gadgets.json.stringify(item);
    list.justUpdated = list.currentlyEditing;
    wave.getState().submitDelta(delta);
    list.currentlyEditing = undefined;
    list.editBoxes = [];
  },
  cancelEdit: function()
  {
    list.currentlyEditing = undefined;
    list.editBoxes = [];
    list.stateUpdated();
  },
  showSettingsEditor:function()
  {
    var settings = list.getSettings();

    list.settingToUI(settings);

    _gel("pane_settings").style.display = "";
    _gel("pane_list").style.display = "none";
    gadgets.window.adjustHeight();
  },

  settingToUI: function(settings)
  {
    //Fill sort select
    var sortBy = settings.sortBy;
    var header = _gel("showHeader");
    header.checked = settings.showHeading;
    var selectBox = _gel("sortBySelect");
    selectBox.options.length = 0;
    selectBox.options[0] = new Option("unsorted", "");
    for(var columnNr = 0; columnNr < settings.columns.length; columnNr++)
    {
      var column = settings.columns[columnNr];
      var opt = new Option(column.name);
      selectBox.options[selectBox.options.length] = opt;
      if(column.name == sortBy)opt.selected = true;
    }

    // Fill list of columns
    var tableStr = "<table>";
    tableStr += "<tr><th>name</th><th>display name</th><th>required</th><th> </th><th> </th></tr>";
    for(var columnNr = 0; columnNr < settings.columns.length; columnNr++)
    {
      var column = settings.columns[columnNr];
      if(!column.displayName)column.displayName = column.name;
      tableStr += "<tr class='" + (columnNr%2 ? 'odd' : 'even') + "'><td>" + column.name + "</td><td><input id='displayname_" + column.name + "' value='" + column.displayName + "'/></td>";
      tableStr += "<td><input type='checkbox' id='required_" + column.name + "' " + (column.required ? "CHECKED" : "") + "></td>";
      tableStr += "<td><img onclick='list.moveUpColumn(this)' src='http://list-gadget.googlecode.com/svn/trunk/up.gif'><img  onclick='list.moveDownColumn(this)' src='http://list-gadget.googlecode.com/svn/trunk/down.gif'></td>";
      tableStr += "<td><img onclick='list.deleteColumn(this)' src='http://list-gadget.googlecode.com/svn/trunk/del.gif'></td>";
      tableStr += "</tr>";
    }
    tableStr += "</table>";
    _gel("columns").innerHTML = tableStr;

    _gel("jsonInterface").value = gadgets.json.stringify(settings);
  },
  UiToSettings: function()
  {
    var settings = list.getSettings();
    var table = _gel("columns").getElementsByTagName("table")[0];
    var newColumns = [];
    for(var i = 1;i< table.rows.length; i++)
    {
      if(table.rows[i].deleted)continue;
      var name = table.rows[i].cells[0].innerHTML;
      var column = list.getColumnByName(settings, name) || {"name": name};
      var displayname = _gel("displayname_" + name);
      column.displayName = displayname.value;
      var requiredbox = _gel("required_" + name);
      column.required = requiredbox.checked;
      newColumns.push(column);
    }
    settings.columns = newColumns;
    settings.sortBy = _gel("sortBySelect").value;
    settings.showHeading = _gel("showHeader").checked;
    return settings;
  },
  addColumnInEditor:function()
  {
    var name = prompt("Column name?");
    name = name.replace(/\W/g,"");
    var table = _gel("columns").getElementsByTagName("table")[0];
    if(table == null)return;
    var index = table.tBodies[0].rows.length-1;
    var row = table.tBodies[0].insertRow(index+1);
    row.insertCell(0).innerHTML = name;
    row.insertCell(1).innerHTML = "<input id='displayname_" + name + "' value='" + name + "'/>";
    row.insertCell(2).innerHTML = "<input type='checkbox' id='required_" + name + "' >";
    row.insertCell(3).innerHTML = "<img onclick='list.moveUpColumn(this)' src='http://list-gadget.googlecode.com/svn/trunk/up.gif'><img  onclick='list.moveDownColumn(this)' src='http://list-gadget.googlecode.com/svn/trunk/down.gif'>";
    row.insertCell(4).innerHTML = "<img onclick='list.deleteColumn(this)' src='http://list-gadget.googlecode.com/svn/trunk/del.gif'>";
    gadgets.window.adjustHeight();
  },
  moveUpColumn:function(link)
  {
    var row = link.parentNode.parentNode;

    if(row.previousSibling.className != "")
    {
      var tmp = row.className; row.className = row.previousSibling.className; row.previousSibling.className = tmp;
      row.parentNode.insertBefore(row,row.previousSibling);
    }
  },
  moveDownColumn:function(link)
  {
    var row = link.parentNode.parentNode;

    if(row.nextSibling != null)
    {
      var tmp = row.className; row.className = row.nextSibling.className; row.nextSibling.className = tmp;
      row.parentNode.insertBefore(row.nextSibling, row);
    }
  },
  deleteColumn:function(link)
  {
    var row = link.parentNode.parentNode;
    row.deleted = true;
    row.style.display = 'none';
  gadgets.window.adjustHeight();
  },
  closeSettingsEditor:function()
  {
    _gel("pane_settings").style.display = "none";
    _gel("pane_list").style.display = "";
    gadgets.window.adjustHeight();
  },
  saveSettings:function()
  {
    var settings = list.UiToSettings();
    if(list.settingsTabs._currentTab != null && list.settingsTabs.getSelectedTab().getName() != "JSON")
    {
      // use UI, not json entry
      list.setSettings(settings);
      list.closeSettingsEditor();
      return;
    }else{
      try
      {
        var o = gadgets.json.parse(_gel("jsonInterface").value);
        list.setSettings(o);
        list.closeSettingsEditor();
      }catch(e)
      {
        alert(e);
      }
    }
  },

  getElementPosition:function(elm){
    var posX = 0;
    var posY = 0;

    while(elm != null){
      posX += elm.offsetLeft;
      posY += elm.offsetTop;
      elm = elm.offsetParent;
    }

   return {x:posX,y: posY};

  },
  tabSelected: function()
  {
    setTimeout(function(){gadgets.window.adjustHeight()}, 100);
  },
  init: function () {
    wave.ui.loadCss();

    if (wave && wave.isInWaveContainer()) {
      wave.setStateCallback(list.stateUpdated, this);
      wave.setModeCallback(list.modeUpdated, this);
    }
    list.settingsTabs = new custom.TabSet("tab_placeholder", "View");
    list.settingsTabs.addTab("View", "tab_1", list.tabSelected);
    list.settingsTabs.addTab("Columns", "tab_columns", list.tabSelected);
    list.settingsTabs.addTab("JSON", "tab_advanced", list.tabSelected);
    list.settingsTabs.addTab("About", "tab_about", list.tabSelected);

    wave.ui.makeButton(_gel('showSettings'));
    wave.ui.makeButton(_gel('cancelSettings'));
    wave.ui.makeButton(_gel('saveSettings'));

    gadgets.window.adjustHeight();
  },
  makehtml: function(s) {
    if(!s)return "";
    var result = s.replace(/&/g,"&amp;");
    result = result.replace(/</g,"&lt;");
    result = result.replace(/>/g,"&gt;");
    result = result.replace(/\r\n/g,"<br/>");
    result = result.replace(/\n/g,"<br/>");
    result = result.replace(/\r/g,"<br/>");
    return(result);
  }

}
var custom =
{
  TabSet: function(containerElm, defaultTab){
    var that = this;
    this.addTab = function(name, id, callback)
    {
      var element = _gel(id);
      if(element)
      {
        if(defaultTab != name)element.style.display = 'none';


    var elm = document.createElement("span");
    elm.innerHTML = name;
    elm.addEventListener("click", function(){that._tabClick(id)}, false);
    elm.className = "tab";
    _gel(containerElm).appendChild(elm);

        this._tabs[id] =
        {
          id: id,
          element:element,
          tabHandle: elm,
          name:name,
          getName: function()
          {
            return this.name;
          },
          clicked: function(){}
        };
        if(callback)
        {
          this._tabs[id].clicked = callback;
        }

      }
    };
    this._tabs = {};
    this._currentTab = null;
    this._tabClick = function(id)
    {
      if(this._currentTab)this.getSelectedTab().tabHandle.className = "tab";
      for(var name in this._tabs)
      {
        if(name == id)
        {
          this._tabs[name].element.style.display = '';
          this._currentTab = this._tabs[name].id;
        }else{
          this._tabs[name].element.style.display = 'none';
        }
      }
      this.getSelectedTab().tabHandle.className = "tab selected";
      if(this.getSelectedTab().clicked)this.getSelectedTab().clicked();
    };
    this.getSelectedTab = function()
    {
      return this._tabs[this._currentTab];
    };
  }
}

gadgets.util.registerOnLoadHandler(list.init);
</script>
</head>
<body>
<div id="gadget-wrapper" class="viewing">

  <div id="pane_list" style="display:block;">
    <div id="listDisplay"></div>
    <div class="onlyEditing">
    <a href="javascript:list.newItem()" style="font-size:x-small;">add item</a>
    <div style="float:right;">
    <a id="showSettings" href="javascript:list.showSettingsEditor()" style="font-size:x-small">settings</a>

    </div>
    </div>
  </div>

  <div id="pane_settings" style="display:none;border-style:ridge">
      <div id="tab_placeholder"></div>
      <div id="tab_1">
        <table><tr>
      <td>Sorted by:</td><td><select id="sortBySelect"></select></td>
    </tr><tr>
      <td>Show header:</td><td><input type="checkbox" id="showHeader"/></td>
    </tr></table>
    </div>
    <div id="tab_columns" >
    <div id="columns"></div>
    <div >
      <a href="javascript:list.addColumnInEditor()" style="font-size:x-small;">add column</a>
    </div>
    </div>
    <div id="tab_advanced" >
      Please do not use this pane if you are not sure what JSON is:<br/>
    <textarea id="jsonInterface" style="width:90%" rows="5"></textarea><br/>
    </div>
    <div id="tab_about" >
      List gadget v0.2<br/>
      This gadget was built by Teun Duynstee. If you like it and want to express you thanks, I love to receive postcards:<blockquote><small>Teun Duynstee<br/>Zonstraat 77<br/>3581MR Utrecht<br/>The Netherlands</small></blockquote>
    You can wave me <a href="https://wave.google.com/wave/waveref/googlewave.com/w+MdPkba_QA" target="_blank">feedback</a>
    </div>
    <div style="text-align:right;">
      <a href="javascript:list.closeSettingsEditor()" style="font-size:x-small;" id="cancelSettings" >cancel</a>&nbsp;<a href="javascript:list.saveSettings()" style="font-size:x-small;" id="saveSettings" >save settings</a>
    </div>
  </div>

</div>

<div>
	<select id="newMode">
	  <option value="1">PLAYBACK</option>
	  <option value="2">EDIT</option>
	  <option value="3">VIEW</option>
	</select>
	<button onclick="_triggerModeCallBack(_gel('newMode').value)">Call ModeCallback</button>
</div>
</body>
</html>
